export module enum_array;

import std;

export 
template< typename Enum, typename _Ty >
    requires std::is_enum_v<Enum>&& requires { Enum::Max; }
class EnumArray {
private:
    std::array<_Ty, size_t(Enum::Max)> data;
public:
    using _Enum = Enum;
    constexpr static size_t size = size_t(Enum::Max);

    template<typename... Args>
        requires (sizeof...(Args) == size) && (std::is_convertible_v<Args, _Ty> && ...)
    EnumArray(Args&&... args) : data{std::forward<Args>(args)...} {};
    EnumArray() = default;

    template<typename = void>
        requires std::is_copy_constructible_v<_Ty>
    EnumArray(const EnumArray& other) : data(other.data) {};

    template<typename = void>
        requires std::is_move_constructible_v<_Ty>
    EnumArray(EnumArray&& other) : data(std::move(other.data)) {};

    template<typename = void>
        requires std::is_copy_assignable_v<_Ty>
    EnumArray& operator=(const EnumArray& other){
        data = other.data;
        return *this;
    };
    template<typename = void>
        requires std::is_move_assignable_v<_Ty>
    EnumArray& operator=(EnumArray&& other) {
        data = std::move(other.data);
        return *this;
    };

    constexpr _Ty& operator[](Enum index) noexcept {
        return data[size_t(index)];
    };
    constexpr const _Ty& operator[](Enum index) const noexcept {
        return data[size_t(index)];
    };

    constexpr _Ty& at(Enum index){
        return data.at(size_t(index));
    };
    constexpr const _Ty& at(Enum index) const{
        return data.at(size_t(index));
    };

    constexpr auto begin() noexcept{
        return data.begin();
    };

    constexpr auto cbegin() const noexcept{
        return data.cbegin();
    };

    constexpr auto end() noexcept{
        return data.end();
    };

    constexpr auto cend() const noexcept{
        return data.cend();
    };
};
